import debugFactory from "debug";
const debug = debugFactory("core:loader/index");
import { join } from "node:path";
import FileLoader from "./file_loader.js";
import ContextLoader from "./context_loader.js";
import { defaultCamelize, loadFile, readJSONSync } from "../utils/index.js";
import timing from "../utils/timing.js";
const resolve = import.meta.resolve;
export default class Loader {
  constructor(app) {
    this.mixins = [
      "config",
      "plugin",
      "middleware",
      "controller",
      "service",
      "router",
    ];
    this.app = app;
    this.pkg = readJSONSync(join(this.app.root, "package.json"));
    this.timing = timing;
    this._mixin();
  }
  get env() {
    return this.app.env;
  }
  get root() {
    return this.app.root;
  }

  getLoadName(item) {
    return `load${defaultCamelize(item, "upper")}`;
  }

  async init() {
    const mixins = this.mixins;
    for (let i = 0; i < mixins.length; i++) {
      const loadName = this.getLoadName(mixins[i]);
      if (this[loadName]) {
        debug("调用了mixin中的loadName: %s", loadName);
        const handdle = await this[loadName];
        await handdle(this);
      }
    }
  }

  /**
   * @function loadToApp 装载到 Application
   * @param {String|Array} directory - see {@link FileLoader}
   * @param {String} property - see {@link FileLoader}
   * @param {Object} options - see {@link FileLoader}
   */
  async loadToApp(directory, property, options) {
    const timeKey = `加载${String(property)}到application时间`
    const target = Object.create(null);
    options = Object.assign(
      Object.create(null),
      {
        directory,
        target,
        inject: this.app,
      },
      options
    );
    timing.time(timeKey);
    await (new FileLoader(options)).load();
    timing.timeEnd(timeKey);
    this.app[property] = target;
    return target;
  }

  /**
   * @function loadToContext 加载到上下文
   * @param {String|Array} directory - see {@link ContextLoader}
   * @param {String} property - see {@link ContextLoader}
   * @param {Object} options - see {@link ContextLoader}
   */
  async loadToContext(directory, property, options) {
    options = Object.assign(
      {},
      {
        target: {},
        directory,
        property,
        inject: this.app,
      },
      options
    );

    const timeKey = `加载"${String(property)}"到 context`;
    this.timing.time(timeKey);
    await new ContextLoader(options).load();
    this.timing.timeEnd(timeKey);
  }

  /**
   * @member {FileLoader} Loader#FileLoader
   */
  get FileLoader() {
    return FileLoader;
  }

  /**
   * @member {ContextLoader} Loader#ContextLoader
   */
  get ContextLoader() {
    return ContextLoader;
  }

  /**
   * @function _mixin 私有方法，
   * @description 合并指定的mixin
   */
  _mixin() {
    // 合并mixin
    this.mixins.forEach((item) => {
      const loadName = this.getLoadName(item);
      this[loadName] = loadFile(resolve(`./mixin/${item}.js`));
    });
  }
}
